/*
 * Copyright (c) 2008 centrevillage <centrevillage@gmail.com>
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#ifndef  __PROJECTREADER_H__
#define  __PROJECTREADER_H__

// std
#include <string>
#include <fstream>
#include <map>
#include <iterator>

// boost
#include <boost/shared_ptr.hpp>
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>

// seq
#include "def.h"
#include "Project.h"
#include "MidiException.h"

namespace seq {

class ProjectReader {
public:
    ProjectReader();
    void init();
    boost::shared_ptr<Project> readSMF(const boost::filesystem::path& filepath);
    boost::shared_ptr<Project> readRVF(const boost::filesystem::path& filepath);
    boost::shared_ptr<Project> read(const boost::filesystem::path& filepath);
private:
    typedef std::pair<BYTE, std::pair<unsigned, BYTE> > map_pair;
    typedef std::multimap< BYTE, std::pair<unsigned, BYTE> > map_type;

    struct TrackTmp {
        typedef ProjectReader::map_type map_type;
        TrackTmp(UINT32 trkNo = 0) {
            trkNo_ = trkNo;
            delta_ = 0;
            deltaSum_ = 0;
            status_ = 0;
            data1_ = 0;
            data2_ = 0;
            trackChannel_ = 0xFF;
            rpnOrNrpnLSB_ = 0;
            rpnOrNrpnMSB_ = 0;
            lsb_ = 0;
            msb_ = 0;
            dataMSB_ = 0;
            dataLSB_ = 0;
            isPrevMSB_ = false;
            prevDeltaSum_ = 0;
            bsLsb_ = 0;
            bsMsb_ = 0;
            isPrevBSMSB_ = false;
            prevBSDeltaSum_ = 0;
        }

        UINT32 trkNo_;
        UINT32 delta_;
        UINT32 deltaSum_;
        BYTE status_;
        BYTE data1_;
        BYTE data2_;
        BYTE trackChannel_;
        BYTE rpnOrNrpnLSB_;
        BYTE rpnOrNrpnMSB_;
        BYTE lsb_;
        BYTE msb_;
        BYTE dataMSB_;
        BYTE dataLSB_;
        bool isPrevMSB_;
        UINT32 prevDeltaSum_;
        BYTE bsLsb_;
        BYTE bsMsb_;
        bool isPrevBSMSB_;
        UINT32 prevBSDeltaSum_;
        map_type noteMap_;
    };

    boost::shared_ptr<Project> readSMF1Main();
    boost::shared_ptr<Project> readSMF0Main();

    bool readData(char& c);
    bool readVariableData_(UINT32& data);
    bool readChank();
    void readHeaderChank();
    bool readShortMsg(TrackTmp& t);
    void readExtraMsg(TrackTmp& t);
    void setRemainData(TrackTmp& t);
    void setTrackChForSMF1(TrackTmp& t);
    size_t getTrackNoFromStatus(BYTE status);

    boost::filesystem::ifstream in_;

    UINT32 type_;
    UINT32 dataSize_;
    UINT32 format_;
    UINT32 nTracks_;
    UINT32 tickPer4_;

    boost::shared_ptr<Project> project_;

    friend class ProjectReader::TrackTmp;
};

} // end of namespace seq
#endif   /* ----- #ifndef __PROJECTREADER_H__  ----- */
